
public class Surroundings 
{
	private short surroundings = 0;
	private byte center = 0;
	private byte turn = 0;
	private int column = 0;
	private int row = 0;
	
	/* Evaluation values */
	public static final int WIN = 500000;
	public static final int LAST_ROW = 50000;
	public static final int DIAGONAL_DEFENSE = 300;
	public static final int PARALLEL_DEFENSE = 100;
	public static final int ATTACKING = 200;
	public static final int BLOCKING = 100;
	public static final int DEFAULT = 100;
	
	/* Position accessors */
	public static final int TOP = 0;
	public static final int TOP_RIGHT = 2;
	public static final int RIGHT = 4;
	public static final int BOTTOM_RIGHT = 6;
	public static final int BOTTOM = 8;
	public static final int BOTTOM_LEFT = 10;
	public static final int LEFT = 12;
	public static final int TOP_LEFT = 14;
	
	public Surroundings(Grid grid, int position, byte turn)
	{
		row = ((position >> 1) / Grid.SIZE) + 1;
		column = ((position >> 1) % Grid.SIZE) + 1;
		center = grid.getCell(position);
		this.turn = turn;
		
		/* 
		 * Bitshift >> 0
		 * +---+---+---+
		 * |   | X |   |
		 * +---+---+---+
		 * |   |   |   |
		 * +---+---+---+
		 * |   |   |   |
		 * +---+---+---+
		 */
		if(row != Grid.SIZE)
		{
			surroundings |= grid.getCell(position + (Grid.SIZE << 1));
		}
		
		/*
		 * Bitshift >> 2
		 * +---+---+---+
		 * |   |   | X |
		 * +---+---+---+
		 * |   |   |   |
		 * +---+---+---+
		 * |   |   |   |
		 * +---+---+---+
		 */
		if(row != Grid.SIZE && column != Grid.SIZE)
		{
			surroundings |= (grid.getCell(position + ((Grid.SIZE + 1) << 1)) << TOP_RIGHT);
		}
		
		/*
		 * Bitshift >> 4
		 * +---+---+---+
		 * |   |   |   |
		 * +---+---+---+
		 * |   |   | X |
		 * +---+---+---+
		 * |   |   |   |
		 * +---+---+---+
		 */
		if(column != Grid.SIZE)
		{
			surroundings |= (grid.getCell(position + 2) << RIGHT);
		}
		
		/*
		 * Bitshift >> 6
		 * +---+---+---+
		 * |   |   |   |
		 * +---+---+---+
		 * |   |   |   |
		 * +---+---+---+
		 * |   |   | X |
		 * +---+---+---+
		 */
		if(row != 1 && column != Grid.SIZE)
		{
			surroundings |= (grid.getCell(position - ((Grid.SIZE + 1) << 1)) << BOTTOM_RIGHT);
		}
		
		/*
		 * Bitshift >> 8
		 * +---+---+---+
		 * |   |   |   |
		 * +---+---+---+
		 * |   |   |   |
		 * +---+---+---+
		 * |   | X |   |
		 * +---+---+---+
		 */
		if(row != 1)
		{
			surroundings |= (grid.getCell(position - (Grid.SIZE << 1)) << BOTTOM);
		}

		/*
		 * Bitshift >> 10
		 * +---+---+---+
		 * |   |   |   |
		 * +---+---+---+
		 * |   |   |   |
		 * +---+---+---+
		 * | X |   |   |
		 * +---+---+---+
		 */
		if(row != 1 && column != 1)
		{
			surroundings |= (grid.getCell(position - ((Grid.SIZE - 1) << 1)) << BOTTOM_LEFT);
		}
		
		/*
		 * Bitshift >> 12
		 * +---+---+---+
		 * |   |   |   |
		 * +---+---+---+
		 * | X |   |   |
		 * +---+---+---+
		 * |   |   |   |
		 * +---+---+---+
		 */
		if(column != 1)
		{
			surroundings |= (grid.getCell(position - 2) << LEFT);
		}
		
		/*
		 * Bitshift >> 14
		 * +---+---+---+
		 * | X |   |   |
		 * +---+---+---+
		 * |   |   |   |
		 * +---+---+---+
		 * |   |   |   |
		 * +---+---+---+
		 */
		if(row != Grid.SIZE && column != 1)
		{
			surroundings |= (grid.getCell(position + ((Grid.SIZE - 1) << 1)) << TOP_LEFT);
		}
	}
	
	/**
	 * Get the value of the cell based on its surroundings
	 * @return
	 */
	public int getValue()
	{
		if(center == Grid.EMPTY)
		{
			return 0;
		}

		// WIN
		if((row == Grid.SIZE && center == Grid.WHITE) || (row == 1 && center == Grid.BLACK))
		{
			return WIN;
		}
		
		int score = 0;
		
		// LAST ROW
		if((row == Grid.SIZE - 1 && center == Grid.WHITE) || (row == 2 && center == Grid.BLACK))
		{
			score += LAST_ROW;
		}
		
		// TOP-LEFT
		if(getTopLeft() == center)
		{
			score += DIAGONAL_DEFENSE;
		}
		else if(getTopLeft() == Grid.BLACK && center == Grid.WHITE && turn == Grid.WHITE)
		{
			score += ATTACKING;
		}
		
		// TOP
		if(getTop() == (center ^ 3) && center == Grid.WHITE && turn == Grid.BLACK)
		{
			score += BLOCKING;
		}
		
		// TOP-RIGHT
		if(getTopRight() == center)
		{
			score += DIAGONAL_DEFENSE;
		}
		else if(getTopRight() == (center ^ 3) && center == Grid.WHITE && turn == Grid.WHITE)
		{
			score += ATTACKING;
		}
		
		// RIGHT
		if(getRight() == center)
		{
			score += PARALLEL_DEFENSE;
		}
		
		// BOTTOM-RIGHT
		if(getBottomRight() == center)
		{
			score += DIAGONAL_DEFENSE;
		}
		else if(getBottomRight() == (center ^ 3) && center == Grid.BLACK && turn == Grid.BLACK)
		{
			score += ATTACKING;
		}
		
		// BOTTOM
		if(getBottom() == (center ^ 3) && center == Grid.BLACK && turn == Grid.BLACK)
		{
			score += BLOCKING;
		}
		
		// BOTTOM-LEFT
		if(getBottomLeft() == center)
		{
			score += DIAGONAL_DEFENSE;
		}
		else if(getBottomLeft() == (center ^ 3) && center == Grid.BLACK && turn == Grid.BLACK)
		{
			score += ATTACKING;
		}
		
		// LEFT
		if(getLeft() == center)
		{
			score += PARALLEL_DEFENSE;
		}
		
		// DEFAULT
		if(center == Grid.WHITE)
		{
			if(row == 1 || row == 2)
			{
				score += DEFAULT;
			}
			else if(row == 3 || row == 4)
			{
				score += DEFAULT << 1;
			}
			else if(row == 5 || row == 6)
			{
				score += DEFAULT << 3;
			}
		}
		else
		{
			if(row == 7 || row == 8)
			{
				score += DEFAULT;
			}
			else if(row == 5 || row == 6)
			{
				score += DEFAULT << 1;
			}
			else if(row == 3 || row == 4)
			{
				score += DEFAULT << 2;
			}
		}
		
		return score;
	}
	
	public byte getCenter()
	{
		return center;
	}
	
	public byte getTop()
	{
		return (byte) (surroundings & 3);
	}
	
	public byte getTopRight()
	{
		return (byte) ((surroundings >> TOP_RIGHT) & 3);
	}
	
	public byte getRight()
	{
		return (byte) ((surroundings >> RIGHT) & 3);
	}
	
	public byte getBottomRight()
	{
		return (byte) ((surroundings >> BOTTOM_RIGHT) & 3);
	}
	
	public byte getBottom()
	{
		return (byte) ((surroundings >> BOTTOM) & 3);
	}
	
	public byte getBottomLeft()
	{
		return (byte) ((surroundings >> BOTTOM_LEFT) & 3);
	}
	
	public byte getLeft()
	{
		return (byte) ((surroundings >> LEFT) & 3);
	}
	
	public byte getTopLeft()
	{
		return (byte) ((surroundings >> TOP_LEFT) & 3);
	}
}
